#ifndef __MAZE_H__
#define __MAZE_H__

#define MAZE_MAX_HOME_CELLS 4096
#define MAZE_MAX_JAIL_CELLS 4096

/* defines cell state bits */
#define BELONGS_TO_TEAM_2	0x01
#define IS_FREE        		0x02
#define IS_WALL   			0x04
#define IS_A_HOME			0x08
#define IS_A_JAIL   		0x10
#define HAS_BOMBS    		0x20
#define HAS_A_FLAG   		0x40
#define HAS_A_PLAYER    	0x80
/* cell extra bits */
#define OCCUPIED_BY_TEAM_1	0x01
#define OCCUPIED_BY_TEAM_2	0x02
#define HAS_FLAG_1			0x04
#define HAS_FLAG_2			0x08
#define HAS_BOMB_1			0x10
#define HAS_BOMB_2			0x20

// POSITION
typedef struct PositionStruct {
	uint16_t row;
	uint16_t col;
} Position;

// CELL
typedef struct CellStruct {
	Position pos;
	uint64_t state;
	uint64_t extra;
} Cell;

// MAZE
typedef struct MazeStruct {
	uint16_t numCells;
	uint16_t numRows;
	uint16_t numCols;
	uint16_t numHome1;
	uint16_t numHome2;
	uint16_t numJail1;
	uint16_t numJail2;
	// pointers
	Cell  *cells;
	Cell  *home1[MAZE_MAX_HOME_CELLS];
	Cell  *home2[MAZE_MAX_HOME_CELLS];
	Cell  *jail1[MAZE_MAX_JAIL_CELLS];
	Cell  *jail2[MAZE_MAX_JAIL_CELLS];
} Maze;

// methods for accessing a particular cell
static inline Cell * getMazeRow(Maze *m, uint16_t rIdx)
{
	return m->cells + (rIdx * m->numCols);
}
// wrapper method for accessing a particular cell
static inline Cell * getMazeCell(Maze *m, uint16_t rIdx, uint16_t cIdx)
{
	return  getMazeRow(m, rIdx) + cIdx;
}

/* Cell accessor/mutator methods */
#define CELL_BELONGS_TO_TEAM_1(c) !CELL_BELONGS_TO_TEAM_2
#define CELL_SET_TEAM_1(c) (c->state) // 0, default
#define CELL_BELONGS_TO_TEAM_2(c) (c->state & BELONGS_TO_TEAM_2)
#define CELL_SET_TEAM_2(c) (c->state |= BELONGS_TO_TEAM_2)

#define CELL_IS_FREE(c) (c->state & IS_FREE)
#define CELL_IS_WALL(c) (c->state & IS_WALL)
#define CELL_IS_A_JAIL(c) (c->state & IS_A_JAIL)
#define CELL_IS_JAIL_1(c) ( CELL_BELONGS_TO_TEAM_1 && CELL_IS_A_JAIL )
#define CELL_IS_JAIL_2(c) ( CELL_BELONGS_TO_TEAM_2 && CELL_IS_A_JAIL )
#define	CELL_IS_A_HOME(c) ( c->state & IS_A_HOME )
#define CELL_IS_HOME_1(c) ( CELL_BELONGS_TO_TEAM_1 && CELL_IS_A_HOME )
#define CELL_IS_HOME_2(c) ( CELL_BELONGS_TO_TEAM_2 && CELL_IS_A_HOME )

#define CELL_TOGGLE_HAS_A_PLAYER(c) (c->state ^= HAS_A_PLAYER)
#define CELL_TOGGLE_HAS_BOMBS(c) (c->state ^= HAS_BOMBS)
#define CELL_TOGGLE_HAS_FLAG(c) (c->state ^= HAS_A_FLAG)

#define CELL_OCCUPIED_BY_TEAM_1(c) (c->extra & OCCUPIED_BY_TEAM_1)
#define CELL_OCCUPIED_BY_TEAM_2(c) (c->extra & OCCUPIED_BY_TEAM_2)
#define CELL_HAS_FLAG_1(c) (c->extra & HAS_FLAG_1)
#define CELL_HAS_FLAG_2(c) (c->extra & HAS_FLAG_2)
#define CELL_HAS_BOMB_1(c) (c->extra & HAS_BOMB_1)
#define CELL_HAS_BOMB_2(c) (c->extra & HAS_BOMB_2)

#define CELL_TOGGLE_HAS_FLAG_1(c) (c->extra ^= HAS_FLAG_1)
#define CELL_TOGGLE_HAS_FLAG_2(c) (c->extra ^= HAS_FLAG_2)
#define CELL_TOGGLE_HAS_BOMB_1(c) (c->extra ^= HAS_BOMB_1)
#define CELL_TOGGLE_HAS_BOMB_2(c) (c->extra ^= HAS_BOMB_2)

#define CELL_BLAST(c) (c->state ^= (IS_WALL | IS_FREE))



// declare methods in maze.c
struct PlayerStruct;
Maze * load( char* filepath );
uint16_t maze_dump_cell(Cell *c);
uint16_t maze_player_add(Maze *m, struct PlayerStruct *p);
Position maze_player_move(Maze *m, struct PlayerStruct *p, Position pos);

#endif
